export function isObject(obj) {
  return typeof obj === "object" && obj != null;
}

/* nextTick */
let callbacks = [];
let waiting = false;
function flushCallback() {
  for (let i = 0; i < callbacks.length; i++) {
    let callback = callbacks[i];
    callback();
  }
  waiting = false;
}

// 连续多次次调用 nextTick 时，第一次会将 cb 存放起来，然后进入 promise 微任务，
// 后续调用 nextTick 是同步任务，会将 cb 存放起来，此时 waiting 为true，无法再开启微任务；
// 所有同步任务执行完后，微任务再执行
// 因此多次同步调用 nextTick 只会产生一个微任务，实现异步批量更新；
export function nextTick(cb) {
  callbacks.push(cb); // cb 是渲染逻辑，
  // 采用向下兼容的方式 promise ==> mutationObserver ===> setImmediate ===> setTimeout
  if (!waiting) {
    waiting = true;
    Promise.resolve().then(flushCallback);
  }
}

const LIFECYCLE_HOOKS = ["beforeCreate", "created", "beforeMount", "mounted"];

const strats = {}; // 策略集合

LIFECYCLE_HOOKS.forEach((hook) => {
  strats[hook] = mergeHook;
});

// 钩子的合并策略
// 第一次合并时 parentVal 一定没有值
function mergeHook(parentVal, childVal) {
  if (childVal) {
    if (parentVal) {
      return parentVal.concat(childVal);
    } else {
      return [childVal];
    }
  } else {
    return parentVal;
  }
}

// 将 Vue.component 定义的组件 放到原型上，不进行覆盖
// 因此，当内部组件与全局组件同名时，优先使用内部组件，找不到时再使用全局组件
// strats.components 存放局部组件，不覆盖全局同名组件
strats.components = function (parentVal, childVal) {
  const res = Object.create(parentVal);
  if (childVal) {
    for (let k in childVal) {
      res[k] = childVal[k];
    }
  }
  return res;
};
export function mergeOptions(parent, child) {
  // 三种情况:
  // 1). parent 有 child 无， 就用 parent
  // 2). parent 有 child 有， 就用 child
  // 3). 自定义
  const options = {};
  for (let key in parent) {
    mergeField(key);
  }
  for (let key in child) {
    if (!parent.hasOwnProperty(key)) {
      mergeField(key);
    }
    mergeField(key);
  }

  function mergeField(key) {
    // 对 hook 的处理
    if (strats[key]) {
      return (options[key] = strats[key](parent[key], child[key])); //  strats[key] 即 mergeHook 或 components 函数
    }
    // 非 hook 的处理
    if (isObject(parent[key]) && isObject(child[key])) {
      options[key] = { ...parent[key], ...child[key] }; // 用后者替换前者
    } else {
      if (child[key]) {
        options[key] = child[key];
      } else {
        options[key] = parent[key];
      }
    }
  }
  return options;
}
// 返回一个函数，参数为tag，tag 在 str 中的就是 true ，否则是 false
function makeUp(str) {
  const map = {};
  str.split(",").forEach((tagName) => {
    map[tagName] = true;
  });
  return (tag) => map[tag] || false;
}

// 判断某个标签是否是预先定义好的原始标签
export const isReservedTag = makeUp("a,p,div,ul,li,span,input,button");
